/**
 *
 * @name handlers
 * @namespace
 */

/**
 *
 * @name impl
 * @namespace
 */

var sys = require('sys'),
    util = require('./tools/util'),
    connections = [],
    defaultOptions = {
        host: 'localhost',
        port: 5672,
        login: 'guest',
        password: 'guest',
        mechanism: 'AMQPLAIN',
        provider: 'rabbitmq',
        locale: 'en_US',
        virtualHost: '/',
        capabilities: '',
        insist: false,
        outOfBand: '',
        channelMax: 65536,
        clientProperties: {
            version: '0.0.1',
            platform: 'node',
            information: 'no',
            product: 'node.amqp'
        }
    },
    connections = [],
    connMap = [];
function access(/**impl.Connection*/channel){
    var Access = require('./amqp/access').Access;
    return new Access(channel);
}
/**
 * Create new connection.
 *
 * @param cfg
 * @param {String} [cfg.host="localhost"]
 * @param {Number} [cfg.port=5672]
 * @param {String} [cfg.login="guest"]
 * @param {String} [cfg.password="guest"]
 * @param {String} [cfg.mechanism="AMQPLAIN"]
 * @param {String} [cfg.provider="rabbitmq"]
 * @param {String} [cfg.locale="en_US"]
 * @param {String} [cfg.virtualHost="/"]
 * @param {String} [cfg.capabilities=""]
 * @param {Boolean} [cfg.insist=FALSE]
 * @param {String} [cfg.outOfBand=""]
 * @param {Object} [clientProperties={ version: "0.0.1", platform: "node", information: "no",
 *      product: "node.amqp" }]
 * @returns {impl.Connection} Connection instance
 */
function connection(/**Object*/cfg)/**impl.Connection*/{
    cfg = util.mix(cfg, defaultOptions);
    var Connection = require('./amqp/connection').Connection,
        connection = new Connection(cfg);
    connection.connect(cfg.port, cfg.host);
    return connection;
}
/**
 * Create new channel.
 * <br>
 * If possible, open channel in existsing connection, otherwise create new connection.
 *
 * @param cfg
 * @param {String} [cfg.host="localhost"]
 * @param {Number} [cfg.port=5672]
 * @param {String} [cfg.login="guest"]
 * @param {String} [cfg.password="guest"]
 * @param {String} [cfg.mechanism="AMQPLAIN"]
 * @param {String} [cfg.provider="rabbitmq"]
 * @param {String} [cfg.locale="en_US"]
 * @param {String} [cfg.virtualHost="/"]
 * @param {String} [cfg.capabilities=""]
 * @param {Boolean} [cfg.insist=FALSE]
 * @param {String} [cfg.outOfBand=""]
 * @param {Object} [clientProperties={ version: "0.0.1", platform: "node", information: "no",
 *      product: "node.amqp" }]
 * @returns {impl.Channel} Channel instance
 */
function channel(/**Object*/cfg)/**impl.Channel*/{
    var Channel = require('./amqp/channel').Channel;
    if (connections.length && connections[connections.length - 1].channelMax >
            connections[connections.length - 1].channelCounter) {
        var connId = connections.length - 1, conn = connections[connId],
            channel = new Channel(conn);
        bindChannel(connId, conn, channel);
    } else {
        var conn = connection(cfg), channel = new Channel(conn),
            connId = connections.push(conn) - 1;
        conn.addListener('tune', function(channelMax){
            connMap[connId] = {
                channelMax: channelMax,
                channelCounter: 0
            };
        });
        conn.addListener('open', function(){
            bindChannel(connId, conn, channel);
        });
    }
    return channel;
}
function bindChannel(connId, conn, channel){
    channel.channelId = ++ connMap[connId].channelCounter;
    channel.open({ outOfBand: cfg.outOfBand });
}
/**
 * Return consumer by type.
 *
 * @param channel
 * @param type
 * @param tag
 * @return
 */
function consumer(/**impl.Channel*/channel, /**String*/type, /**String*/tag)/**handlers.Consumer*/{
    var consumer;
    switch (type) {
    case 'basic':
        consumer = basic(channel, tag);
        break;

    case 'file':
        consumer = file(channel, tag);
        break;

    case 'stream':
        consumer = stream(channel, tag);
        break;

    default:
        throw new Error('Unknown consumer type');
    }
    return consumer;
}
/**
 * Create new exchange.
 *
 * @param channel
 */
function exchange(/**impl.Channel*/channel){
    var Exchange = require('./amqp/exchange').Exchange;
    return new Exchange(channel);
}

function queue(/**impl.Channel*/channel)/**impl.Queue*/{
    var Queue = require('./amqp/queue').Queue;
    return new Queue(channel);
}

function basic(/**impl.Channel*/channel, /**String*/consumerTag)/**impl.Basic*/{
    var Basic = require('./amqp/basic').Basic,
        consumer = new Basic(channel);
    if (undefined != consumerTag) {
        consumer.consumerTag = consumerTag;
    }
    return consumer;
}

function file(/**impl.Channel*/channel, /**String*/consumerTag)/**impl.File*/{
    var File = require('./amqp/file').File,
        consumer = new File(channel);
    if (undefined != consumerTag) {
        consumer.consumerTag = consumerTag;
    }
    return consumer;
}

function stream(/**impl.Channel*/channel, /**String*/consumerTag)/**impl.Stream*/{
    var Stream = require('./amqp/stream').Stream,
        consumer = new Stream(channel);
    if (undefined != consumerTag) {
        consumer.consumerTag = consumerTag;
    }
    return consumer;
}

function dtx(/**impl.Channel*/channel)/**impl.Dtx*/{
    var Dtx = require('./amqp/dtx').Dtx;
    return new Dtx(channel);
}

function tx(/**impl.Channel*/channel)/**impl.Tx*/{
    var Tx = require('./amqp/tx').Tx;
    return new Tx(channel);
}

function tunnel(/**impl.Connection*/conn)/**impl.Tunnel*/{
    var Tunnel = require('./amqp/tunnel').Tunnel;
    return new Tunnel(conn);
}

exports.access = access;
exports.connection = connection;
exports.channel = channel;
exports.exchange = exchange;
exports.queue = queue;
exports.dtx = dtx;
exports.consumer = consumer;
exports.basic = basic;
exports.file = file;
exports.stream = stream;
exports.tunnel = tunnel;
exports.tx = tx;
